// miniloop, a simple live looper
//
// Copyright (C) 2007 Nick Thomas
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the <organization> nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY NICK THOMAS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL NICK THOMAS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef MINILOOP_ENGINE_HPP
#define MINILOOP_ENGINE_HPP

#include <alsa/asoundlib.h>
#include <glibmm/ustring.h>
#include <jack/jack.h>
#include <jack/ringbuffer.h>
#include <samplerate.h>
#include <vector>

namespace miniloop
{
    class Engine
    {
    public:
        Engine();

	// Initializes JACK.
        bool init(char *&error);

	// Loads the given file into the given loop slot. Returns true on
	// success, and false on error. Sets an error message on error.
	bool load_loop(int id, Glib::ustring filename, Glib::ustring &error);

	// Returns the current playback position, in samples. Used by the GUI.
	// XXX: This may not work on SMP systems where an integer read is not
	//      atomic. Fix this if it becomes an issue.
	int get_playback_frame() { return m_loop_frame; }

        // Stops the synthesis thread and shuts down JACK.
        ~Engine();

    private:
        // A wrapper that calls ((Engine)arg)->jack_process(nframes).
        static int jack_processing_callback(jack_nframes_t nframes,
                                            void *arg);
	static int jack_buffer_size_callback(jack_nframes_t nframes, void *arg);

        // Synthesis thread callback.
        int jack_process(jack_nframes_t nframes);
	void seq_process();
	void copy_audio(float *dest, float *src, int start, int length);

	// Looping-related state.
	float *m_loop_data; // All of the loop contents, interpolated
	int m_loop_frame;

	// SRC-related state.
	static long src_callback(void *cb_data, float **data);
	SRC_STATE *m_src_state;
	int m_src_pitch; // between 0 and 127
	int m_last_received_pitch;
	bool m_pitch_is_held;

        // JACK-related state.
	struct JackStereoPort
	{
	    jack_port_t *left, *right;
	};

	bool m_jack_initialized;
	jack_client_t *m_jack_client;
	std::vector<JackStereoPort> m_audio_ports;
	float *m_jack_temp_buffer;

	// ALSAseq-related state.
	bool m_seq_initialized;
	snd_seq_t *m_seq;
	int m_seq_id;
	int m_seq_port;

	// State related to loop loading.
	jack_ringbuffer_t *m_loop_load_ringbuf;
	float *m_raw_data;
	float *m_data_L;
	float *m_data_R;
    };
}

#endif
